package com.cjkj.applet.controller;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONObject;
import com.cjkj.applet.domain.TPatIndex;
import com.cjkj.applet.service.ITPatIndexService;
import com.cjkj.common.core.exception.ServiceException;
import com.cjkj.common.core.web.domain.AjaxResult;
import okhttp3.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;

@RestController
@RequestMapping("/images")
@RefreshScope
public class Sample {

    @Value("${spring.file.path}")
    public String filePath;
    private static final String API_KEY = "ucAEjiSKL8vHx7z44BybdIBS";
    private static final String SECRET_KEY = "k0q0iviV0nR3K5YvlsO6SyioMkLuVrRr";

    /**
     * 识别身份证号码
     */
    private static final String IDCARD_OCR = "idcard";

    /**
     * 身份证识别地址
     */
    private static final String IDCARD_OCR_URL = "https://aip.baidubce.com/rest/2.0/ocr/v1/idcard";

    /**
     * 识别社保卡
     */
    private static final String SOCIAL_SECURITY_CARD = "social_security_card";
    /**
     * 社保卡识别地址
     */
    private static final String SOCIAL_SECURITY_CARD_URL = "https://aip.baidubce.com/rest/2.0/ocr/v1/social_security_card";

    static final OkHttpClient HTTP_CLIENT = new OkHttpClient().newBuilder().build();

    @Resource
    private ITPatIndexService patIndexService;

    @PostMapping("/sfznot")
    public AjaxResult images(@RequestParam("file") MultipartFile files, @RequestParam("diary1")String diary1) throws IOException, JSONException {
        String filename = files.getOriginalFilename();
        files.transferTo(new File(filePath + filename));
        String fileContentAsBase64Urlencoded = getFileContentAsBase64Urlencoded(filePath + filename);

        HashMap<String, String> jbxx = new HashMap<>();
        // 识别身份证
        if(IDCARD_OCR.equals(diary1)) {
            jbxx = getIdCardOcrResult(fileContentAsBase64Urlencoded);
        }

        // 识别社保卡
        if(SOCIAL_SECURITY_CARD.equals(diary1)) {
            jbxx = getSocialSecurityCardOcrResult(fileContentAsBase64Urlencoded);
        }

        // 根据身份证查询患者联系电话
        String idNumber = jbxx.get("sfzNo");
        if(StringUtils.isNotBlank(idNumber)) {
            TPatIndex patIndex = patIndexService.getPatByIdentityNumber(idNumber);
            jbxx.put("phone", patIndex != null ? patIndex.getPhone() : "");
        }

        return AjaxResult.success("识别成功", jbxx);
    }

    /**
     * 身份证信息识别
     * @param fileContentAsBase64Urlencoded
     * @return
     * @throws IOException
     * @throws JSONException
     */
    private static HashMap<String, String> getIdCardOcrResult(String fileContentAsBase64Urlencoded) throws IOException, JSONException {
        MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
        RequestBody body = RequestBody.create(mediaType,"id_card_side=front&image=" + fileContentAsBase64Urlencoded);
        Request request = new Request.Builder()
                .url(IDCARD_OCR_URL + "?access_token=" + getAccessToken())
                .method("POST", body)
                .addHeader("Content-Type", "application/x-www-form-urlencoded")
                .addHeader("Accept", "application/json")
                .build();
        Response response = HTTP_CLIENT.newCall(request).execute();
        JSONObject jsonObject = JSON.parseObject(response.body().string());
        /**
         * 识别状态
         * normal-识别正常
         * reversed_side-身份证正反面颠倒
         * non_idcard-上传的图片中不包含身份证
         * blurred-身份证模糊
         * other_type_card-其他类型证照
         * over_exposure-身份证关键字段反光或过曝
         * over_dark-身份证欠曝（亮度过低）
         * unknown-未知状态
         */
        String imageStatus = jsonObject.getString("image_status");
        if (! "normal".equals(imageStatus)) {
            String errMsg = "";
            switch (imageStatus) {
                case "reversed_side":
                    errMsg = "请上传身份证正面照片！";
                    break;
                case "non_idcard":
                    errMsg = "上传的图片中不包含身份证！";
                    break;
                case "blurred":
                    errMsg = "身份证模糊！";
                    break;
                case "other_type_card":
                    errMsg = "请上传正确的身份证照片！";
                    break;
                case "over_exposure":
                    errMsg = "身份证关键字段反光或亮度过高！";
                    break;
                case "over_dark":
                    errMsg = "身份证亮度过低！";
                    break;
                default:
                    errMsg = "未知错误！";
            }
            throw new ServiceException(errMsg);
        }
        JSONObject wordsResult = jsonObject.getJSONObject("words_result");
        HashMap<String, String> jbxx = new HashMap<>();
        jbxx.put("xm", wordsResult.getJSONObject("姓名").getString("words"));
        String sfzNo = wordsResult.getJSONObject("公民身份号码").getString("words");
        jbxx.put("sfzNo", sfzNo);
        String xbStr = wordsResult.getJSONObject("性别").getString("words");
        jbxx.put("xb", getSex(xbStr, sfzNo));
        // 出生日期
        String birthDay = wordsResult.getJSONObject("出生").getString("words");
        StringBuilder sb;
        if(StringUtils.isBlank(birthDay) && StringUtils.isNotBlank(sfzNo)) {
            // 如果没有识别，则在身份证号码中截取
            sb = new StringBuilder(sfzNo.substring(6, 14));
        } else {
            sb = new StringBuilder(birthDay);
        }
        sb.insert(4, "-").insert(7, "-");
        jbxx.put("cs", sb.toString());
        // 年龄
        if(StringUtils.isNotBlank(birthDay)) {
            jbxx.put("nl", String.valueOf(new Date().getYear() + 1900 - Integer.parseInt(birthDay.substring(0,4))));
        }

        return jbxx;
    }

    /**
     * 社保卡信息识别
     * @param fileContentAsBase64Urlencoded
     * @return
     * @throws IOException
     * @throws JSONException
     */
    private static HashMap<String, String> getSocialSecurityCardOcrResult(String fileContentAsBase64Urlencoded)
            throws IOException, JSONException {
        MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
        RequestBody body = RequestBody.create(mediaType, "image=" + fileContentAsBase64Urlencoded);
        Request request = new Request.Builder()
                .url(SOCIAL_SECURITY_CARD_URL + "?access_token=" + getAccessToken())
                .method("POST", body)
                .addHeader("Content-Type", "application/x-www-form-urlencoded")
                .addHeader("Accept", "application/json")
                .build();
        Response response = HTTP_CLIENT.newCall(request).execute();
        JSONObject jsonObject = JSON.parseObject(response.body().string());
        JSONObject wordsResult = jsonObject.getJSONObject("words_result");

        HashMap<String, String> socialSecurityCardInfo = new HashMap<>();
        // 姓名
        String nameStr = wordsResult.getJSONObject("name").getString("word");
        socialSecurityCardInfo.put("xm", nameStr);
        // 身份证号码
        String idcardNo = wordsResult.getJSONObject("social_security_number").getString("word");
        socialSecurityCardInfo.put("sfzNo", idcardNo);
        if (StringUtils.isBlank(nameStr) || StringUtils.isBlank(idcardNo)) {
            // 如果连名字和身份证号码都没识别出来，提示识别失败
            throw new ServiceException("社保卡识别失败，请检查照片是否规范！");
        }

        // 出生日期
        String birthDay = wordsResult.getJSONObject("birth_date").getString("word");
        if(StringUtils.isBlank(birthDay) && StringUtils.isNotBlank(idcardNo)) {
            // 如果没有识别，则在身份证号码中截取
            StringBuilder sb = new StringBuilder(idcardNo.substring(6, 14)).insert(4, "-").insert(7, "-");
            birthDay = sb.toString();
        } else {
            birthDay = birthDay.replace("年", "-");
            birthDay = birthDay.replace("月", "-");
            birthDay = birthDay.replace("日", "");
        }
        socialSecurityCardInfo.put("cs", birthDay);
        // 性别
        String sex = wordsResult.getJSONObject("sex").getString("word");
        socialSecurityCardInfo.put("xb", getSex(sex, idcardNo));
        // 年龄
        if(StringUtils.isNotBlank(birthDay)) {
            socialSecurityCardInfo.put("nl", String.valueOf(new Date().getYear() + 1900 - Integer.parseInt(birthDay.substring(0,4))));
        }

        return socialSecurityCardInfo;
    }

    /**
     * 判断性别
     * @param sex 证件识别结果中的性别
     * @param idcardNo 证件识别结果中的身份证号码
     * @return
     */
    public static String getSex(String sex, String idcardNo) {
        String xb = "男".equals(sex) ? "1" : "女".equals(sex) ? "2" : "";
        if(StringUtils.isBlank(xb)) {
            if (StringUtils.isNotBlank(idcardNo) && idcardNo.length() == 18) {
                char genderCode = idcardNo.charAt(idcardNo.length() - 2);
                int genderDigit = Integer.parseInt(String.valueOf(genderCode));
                xb = genderDigit % 2 == 0 ? "2" : "1";
            }
        }
        return xb;
    }

    /**
     * 获取文件base64编码
     * @throws IOException IO异常
     */
    static String getFileContentAsBase64(String path) throws IOException {
        byte[] b = Files.readAllBytes(Paths.get(path));
        return Base64.getEncoder().encodeToString(b);
    }

    /**
     * 获取文件base64 UrlEncode编码
     * @param path 文件路径
     * @return base64编码信息，不带文件头
     * @throws IOException IO异常
     */
    static String getFileContentAsBase64Urlencoded(String path) throws IOException {
        return URLEncoder.encode(getFileContentAsBase64(path), "utf-8");
    }

    /**
     * 从用户的AK，SK生成鉴权签名（Access Token）
     *
     * @return 鉴权签名（Access Token）
     * @throws IOException IO异常
     */
    static String getAccessToken() throws IOException, JSONException {
        MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
        RequestBody body = RequestBody.create(mediaType, "grant_type=client_credentials&client_id=" + API_KEY
                + "&client_secret=" + SECRET_KEY);
        Request request = new Request.Builder()
                .url("https://aip.baidubce.com/oauth/2.0/token")
                .method("POST", body)
                .addHeader("Content-Type", "application/x-www-form-urlencoded")
                .build();
        Response response = HTTP_CLIENT.newCall(request).execute();
        return JSON.parseObject(response.body().string()).getString("access_token");
    }

}